package com.kevin.utility.common;

import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;

import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;

public class JedisUtil {

	private static Logger logger = Logger.getLogger(JedisUtil.class);
	private static ShardedJedisPool shardedJedisPool;

	private static JdkSerializationRedisSerializer redisSerializer = new JdkSerializationRedisSerializer();

	public static final String PREFIX_KEY = "_live_";

	
	public static boolean setObject(String key, Object value){
		try {
			set(serialize(PREFIX_KEY+key), serialize(value));
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			return false;
		}
		return true;
	}
	
	/**
	 * 
	 * @param key
	 * @param value
	 * @param timeout 超时时间
	 * @return
	 */
	public static boolean setObject(String key, Object value, int timeout){
		try {
			set(serialize(PREFIX_KEY+key), serialize(value), timeout);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			return false;
		}
		return true;
	}
	
	public static boolean deleteObject(String key) {
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			jedis.del(serialize(PREFIX_KEY + key));
			return true;
		}catch(Exception e){
			logger.error(e.getMessage(), e);
			return false;
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
	}
	
	
	/**
	 * 将对象转成字节码
	 * @param key
	 * @return 当前key对应的字节码
	 * @throws Exception
	 */
	private static byte[] serialize(Object object) throws Exception {
		if (null == object)
			throw new Exception("key is null");
		return redisSerializer.serialize(object);
	}

	/**
	 * 将字节码转成对象
	 * @param object
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private static <T> T deserialize(byte[] value) {
		if(value == null){
			return null;
		}
		return (T) redisSerializer.deserialize(value);
	}


	/**
	 * 将key和value对应。如果key已经存在了，它会被覆盖，而不管它是什么类型。
	 * @param key
	 * @param value
	 * @return
	 */
	public static String setString(String key, String value) {
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			String status = jedis.set(PREFIX_KEY+key, value);
			return status;
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
		return null;
	}
	public static String setString(String key, String value, int seconds) {
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			String status = jedis.setex(key, seconds, value);
			return status;
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
		return null;
	}
	/**
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	private static String set(byte[] key, byte[] value) {
		if(shardedJedisPool == null){
			return null;
		}
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			String status = jedis.set(key, value);
			return status;
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
		return null;
	}
	
	
	private static String set(byte[] key, byte[] value, int timeout) {
		if(shardedJedisPool == null){
			return null;
		}
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			String status = jedis.setex(key, timeout, value);
			return status;
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
		return null;
	}

	/**
	 * 返回key的value。如果key不存在，返回特殊值null。如果key的value不是string，就返回错误，因为GET只处理string类型的
	 * @param key
	 * @return
	 */
	public static String getString(String key) {
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			String value = jedis.get(PREFIX_KEY+key);
			logger.info(String.format("key=[%s]value=[%s]",key,value));
			return value;
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
		return null;
	}
	/**
	 * 返回key的value。如果key不存在，返回特殊值null。如果key的value不是string，就返回错误，因为GET只处理string类型的
	 * @param key
	 * @return
	 */
	public static Object getObject(String key) {
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			String newKey = PREFIX_KEY+key;
			byte[] value = jedis.get(serialize(newKey));
			Object deserialize = deserialize(value);
			
			
			return deserialize;
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
		return null;
	}
	
	
	
	/**
	 * 对key对应的数字做加1操作。如果key不存在，那么在操作之前，这个key对应的值会被置为0。（若值为一个不能表示成数字的字符串，就返回错误）
	 * @param key
	 * @return
	 */
	public static long incr(String key) {
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			return jedis.incr(PREFIX_KEY+key);
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
		return 0;
	}

	/**
	 * 
	 * @param key
	 * @param field
	 * @param value
	 * @return
	 */
	public Long hset(String key, String field, String value) {
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			long count = jedis.hset(PREFIX_KEY+key, field, value);
			return count;
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
		return null;
	}

	public String hget(String key, String field) {
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			return jedis.hget(PREFIX_KEY+key, field);
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
		return null;
	}

	/**
	 * 
	 * @param key
	 * @param strings 可变参数
	 * @return
	 */
	public static Long lpush(String key, String value) {
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			return jedis.lpush(key, value);
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
		return null;
	}

	/**
	 * 这里可以存在字节码
	 * @param key
	 * @param value
	 * @return
	 */
	public static Long lpush(byte[] key, byte[] value) {
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			return jedis.lpushx(key, value);
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
		return null;
	}

	/**
	 * 
	 * @param key
	 * @param list
	 * @return
	 * @throws Exception
	 */
	public static void lpush(byte[] key, List<Object> list) throws Exception {
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			for (Object t : list) {
				jedis.lpush(key, serialize(t));
			}
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
	}

	public static Object rpop(byte[] key) {
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			byte[] status = jedis.rpop(key);
			return deserialize(status);
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
		return null;
	}

	/**
	 * 
	 * @param key
	 * @param string
	 * @return
	 */
	public static Long lpushx(String key, String value) {
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			Long status = jedis.lpushx(PREFIX_KEY+key, value);
			return status;
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
		return null;
	}


	public static List<String> lrange(String key, long start, long end) {
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			List<String> status = jedis.lrange(PREFIX_KEY+key, start, end);
			return status;
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
		return null;
	}

	public static List<byte[]> lrange(byte[] key, long start, long end) {
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			List<byte[]> ret = jedis.lrange(key, start, end);
			return ret;
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
		return null;
	}

	public static Long lrem(byte[] key, long count, byte[] value) {
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			Long ret = jedis.lrem(key, count, value);
			return ret;
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
		return null;
	}

	public Long lrem(String key, long count, String value) {
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			Long ret = jedis.lrem(PREFIX_KEY+key, count, value);
			return ret;
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
		return null;
	}

	public static ShardedJedisPool getShardedJedisPool() {
		return shardedJedisPool;
	}

	public static void setShardedJedisPool(ShardedJedisPool shardedJedisPool) {
		JedisUtil.shardedJedisPool = shardedJedisPool;
	}
	
	
	/**
	 * Title: getLock
	 * Description:
	 * Created On: 2016年3月22日 下午3:22:40
	 * @author kevin02.liu 
	 * @param key
	 * @param expire
	 * @return 
	 */
	public static boolean getLock(String key,int expire){
		long stime = System.currentTimeMillis();
		
		if(shardedJedisPool == null){
			return false;
		}
		if(StringUtil.isNullOrEmpty(key)){
			return false;
		}
		Date date = new Date();
		ShardedJedis jedis = shardedJedisPool.getResource();
		String lockKey = PREFIX_KEY+key;
		logger.info(String.format("key【%S】getLock，时间是=【%s】,时间戳是=【%s】",lockKey,DateUtils.format(date, DateUtils.DEFAULT_FORMAT),date.getTime()));
		try{
			while(true){
				long ret = jedis.setnx(lockKey, String.valueOf(date.getTime()));
				if(ret == 1L){
					jedis.expire(lockKey,expire);
					logger.info(String.format("key【%S】已经取到锁，时间是=【%s】,时间戳是=【%s】",lockKey,DateUtils.format(date, DateUtils.DEFAULT_FORMAT),date.getTime()));
					return true;
				}
				String result = getString(key);
				if(StringUtil.isNullOrEmpty( result)){ //如果超时了，则删掉数据
					logger.info(String.format("key【%S】锁无数据，时间是=【%s】,时间戳是=【%s】",lockKey,DateUtils.format(date, DateUtils.DEFAULT_FORMAT),date.getTime()));
					delLock(key);
				}
				if(!StringUtil.isNullOrEmpty( result) && Long.valueOf(result)+ 20000 < date.getTime()){
					delLock(key);//如果超时了，则删除锁
				}
				if(expire > 0 && new Date().getTime() > date.getTime() + expire*1000){
					logger.info(String.format("key【%S】超时退出，时间是=【%s】,时间戳是=【%s】",lockKey,DateUtils.format(date, DateUtils.DEFAULT_FORMAT),date.getTime()));
					return false;//20秒后不再等待锁
				}
				//让出CPU资源
				Thread.sleep(1);
			}
		}catch(Exception e){
			logger.error(e.getMessage(), e);
			delLock(key);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
			logger.info("getLock = " + (System.currentTimeMillis() - stime));
		}
		return false;
	}
	
	public static boolean getLockOnce(String key, int expire){
		long stime = System.currentTimeMillis();
		if(shardedJedisPool == null){
			return false;
		}
		if(StringUtil.isNullOrEmpty(key)){
			return false;
		}
		Date date = new Date();
		ShardedJedis jedis = shardedJedisPool.getResource();
		String lockKey = PREFIX_KEY+key;
		String dateStr = DateUtils.format(date, DateUtils.DEFAULT_FORMAT);
		logger.info(String.format("key【%S】getLock，时间是=【%s】,时间戳是=【%s】",lockKey,dateStr,date.getTime()));
		try{
			long ret = jedis.setnx(lockKey, lockKey);
			jedis.expire(lockKey,expire);
			if(1L==ret){
				logger.info(String.format("key【%S】已经取到锁，时间是=【%s】,时间戳是=【%s】", lockKey, dateStr, date.getTime()));
				return true;
			}
			String result = getString(key);
			if(StringUtil.isNullOrEmpty( result)){ //如果超时了，则删掉数据
				logger.info(String.format("key【%S】锁无数据，时间是=【%s】,时间戳是=【%s】",lockKey,DateUtils.format(date, DateUtils.DEFAULT_FORMAT),date.getTime()));
				delLock(key);
				return true;
			}
			return false;
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
			logger.info("getLock = " + (System.currentTimeMillis() - stime));
		}
		return false;
	}
	public static boolean delLock(String key){
		long stime = System.currentTimeMillis();
		if(shardedJedisPool == null){
			return false;
		}
		Date date = new Date();
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			String lockKey = PREFIX_KEY+key;
			jedis.del(lockKey);
			logger.info(String.format("key【%S】删除，时间是=【%s】,时间戳是=【%s】",lockKey,DateUtils.format(date, DateUtils.DEFAULT_FORMAT),date.getTime()));
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
			logger.info("delLock = " + (System.currentTimeMillis() - stime));
		}
		return false;
	}

	public static Long expire(String key, int timeOut) {
		ShardedJedis jedis = shardedJedisPool.getResource();
		try{
			Long ret = jedis.expire(PREFIX_KEY+key, timeOut);
			return ret;
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}finally{
			if(null != jedis){
				shardedJedisPool.returnResource(jedis);
			}
		}
		return null;
	}
}